﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace Dapper.Moon
{
    public class SqlServerDatabase : BaseDatabase, IDatabase
    {
        private string _ConnectionString = null;
        private ISqlDialect _SqlDialect = null;

        public SqlServerDatabase(string connectionString)
        {
            _ConnectionString = connectionString;
            _Connection = new SqlConnection(connectionString);
            _SqlDialect = new SqlServerDialect();
            DbId = $"SQLSERVER{DateTime.Now.Ticks}";
        }

        public IDbConnection Connection { get { return _Connection; } }
        public IAop Aop { get { return _Aop; } set { _Aop = value; } }
        public ISqlDialect SqlDialect { get { return _SqlDialect; } }
        public string DbId { get; private set; }

        /// <summary>
        /// 批量添加
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public override int BulkInsert(DataTable table, string tableName)
        {
            if (table?.Rows?.Count == 0) return 0;
            int rowCount = table.Rows.Count;
            Exception exception = null;
            var executingArgs = OnExecuting($"SqlServer BulkInsert,TableName={tableName}", null, ExecuteType.NonQuery);
            bool wasClosed = _Connection.State == ConnectionState.Closed;
            try
            {
                if (wasClosed) _Connection.Open();
                using (SqlBulkCopy bulkcopy = BulkInsertBase(table, tableName))
                {
                    bulkcopy.WriteToServer(table);
                }
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                if (wasClosed) _Connection.Close();
                OnExecuted(executingArgs, exception, rowCount);
            }
            return rowCount;
        }

        private SqlBulkCopy BulkInsertBase(DataTable table, string tableName)
        {
            SqlBulkCopy bulkcopy =
                    _Transaction == null ?
                    new SqlBulkCopy(_ConnectionString, SqlBulkCopyOptions.Default) :
                    new SqlBulkCopy(_Connection as SqlConnection, SqlBulkCopyOptions.Default, _Transaction as SqlTransaction);
            bulkcopy.DestinationTableName = tableName;
            bulkcopy.BulkCopyTimeout = _CommandTimeout;
            bulkcopy.BatchSize = table.Rows.Count;
            foreach (DataColumn item in table.Columns)
            {
                bulkcopy.ColumnMappings.Add(item.ColumnName, item.ColumnName);
            }
            return bulkcopy;
        }

        public override async Task<int> BulkInsertAsync(DataTable table, string tableName)
        {
            if (table?.Rows?.Count == 0) return 0;
            int rowCount = table.Rows.Count;
            Exception exception = null;
            var executingArgs = OnExecuting($"SqlServer BulkInsert,TableName={tableName}", null, ExecuteType.NonQuery);
            bool wasClosed = _Connection.State == ConnectionState.Closed;
            try
            {
                if (wasClosed) _Connection.Open();
                using (SqlBulkCopy bulkcopy = BulkInsertBase(table, tableName))
                {
                    await bulkcopy.WriteToServerAsync(table);
                }
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                if (wasClosed) _Connection.Close();
                OnExecuted(executingArgs, exception, rowCount);
            }
            return rowCount;
        }

        protected override IDbCommand GetCommand(string sql, SqlMapper.IDynamicParameters param = null, CommandType? commandType = null)
        {
            SqlCommand cmd = new SqlCommand(sql, _Connection as SqlConnection);
            cmd.CommandType = commandType == null ? CommandType.Text : (CommandType)commandType;
            cmd.CommandTimeout = _CommandTimeout;
            if (param != null)
            {
                var sqlParams = (param as SqlServerDynamicParameters).Get() as List<SqlParameter>;
                cmd.Parameters.AddRange(sqlParams.ToArray());
            }
            return cmd;
        }

        protected override IDataAdapter GetAdapter(IDbCommand command)
        {
            return new SqlDataAdapter(command as SqlCommand);
        }

        ~SqlServerDatabase() => this.Dispose();

        public void Dispose()
        {
            _Connection?.Close();
        }
    }
}
